package com.zondy.mapgis.psmap.manager.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zondy.mapgis.common.core.exception.ServiceException;
import com.zondy.mapgis.common.core.text.CharsetKit;
import com.zondy.mapgis.common.core.utils.StringUtils;
import com.zondy.mapgis.common.core.utils.bean.BeanValidators;
import com.zondy.mapgis.common.core.utils.file.FileUploadUtils;
import com.zondy.mapgis.config.properties.FileProperties;
import com.zondy.mapgis.domain.psmap.AppWidget;
import com.zondy.mapgis.psmap.manager.mapper.AppWidgetMapper;
import com.zondy.mapgis.psmap.manager.service.IAppWidgetService;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Validator;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class AppWidgetServiceImpl extends ServiceImpl<AppWidgetMapper, AppWidget> implements IAppWidgetService {
   @Autowired
   private AppWidgetMapper appWidgetMapper;
   @Autowired
   private FileProperties fileProperties;
   @Autowired
   protected Validator validator;
   private static final String[] WIDGET_FILE_RESOURCES = new String[]{"widget.js", "widget.css", "setting.js", "setting.css"};

   @Override
   public AppWidget selectAppWidgetByWidgetId(Long widgetId) {
      return this.appWidgetMapper.selectAppWidgetByWidgetId(widgetId);
   }

   @Override
   public List<AppWidget> selectAppWidgetList(AppWidget appWidget) {
      return this.appWidgetMapper.selectAppWidgetList(appWidget);
   }

   @Override
   public AppWidget selectAppWidgetByWidgetName(String widgetName) {
      return this.appWidgetMapper.selectAppWidgetByWidgetName(widgetName);
   }

   @Override
   public boolean checkWidgetNameUnique(String widgetName) {
      int count = this.appWidgetMapper.checkWidgetNameUnique(widgetName);
      return count <= 0;
   }

   @Override
   public int insertAppWidget(AppWidget appWidget) {
      return this.appWidgetMapper.insertAppWidget(appWidget);
   }

   @Override
   public int updateAppWidget(AppWidget appWidget) {
      return this.appWidgetMapper.updateAppWidget(appWidget);
   }

   @Override
   public int deleteAppWidgetByWidgetIds(Long[] widgetIds) {
      return this.appWidgetMapper.deleteAppWidgetByWidgetIds(widgetIds);
   }

   @Override
   public int deleteAppWidgetByWidgetId(Long widgetId) {
      return this.appWidgetMapper.deleteAppWidgetByWidgetId(widgetId);
   }

   @Override
   public byte[] exportWidgets(List<AppWidget> appWidgetList) {
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      ZipOutputStream zip = new ZipOutputStream(outputStream);

       for (AppWidget appWidget : appWidgetList) {
           this.exportWidget(appWidget, zip);
           this.exportWidgetOtherResources(appWidget, zip);
       }

      IOUtils.closeQuietly(zip);
      return outputStream.toByteArray();
   }

   private void exportWidget(AppWidget appWidget, ZipOutputStream zip) {
      Map<String, String> widgetFileList = new HashMap<>();
      String folerName = appWidget.getWidgetName();
      widgetFileList.put(folerName + "/manifest.json", appWidget.getManifest());
      if (StringUtils.isNotEmpty(appWidget.getConfig())) {
         widgetFileList.put(folerName + "/config.json", appWidget.getConfig());
      }

      widgetFileList.forEach((key, value) -> {
         StringWriter sw = new StringWriter();
         sw.append(value);

         try {
            zip.putNextEntry(new ZipEntry(key));
            IoUtil.writeUtf8(zip, false, sw.toString());
            IoUtil.close(sw);
            zip.flush();
            zip.closeEntry();
         } catch (IOException var7) {
            this.log.error("导出微件失败，微件名：" + folerName, var7);
         }

      });
   }

   private void exportWidgetOtherResources(AppWidget appWidget, ZipOutputStream zip) {
      String folerName = appWidget.getWidgetName();
      Map<String, File> fileResourceMap = this.listFileResources("widgets", folerName);
      fileResourceMap.keySet().forEach((filePath) -> {
         try {
            zip.putNextEntry(new ZipEntry(folerName + filePath));
            IoUtil.write(zip, false, FileUtil.readBytes(fileResourceMap.get(filePath)));
            zip.flush();
            zip.closeEntry();
         } catch (IOException var6) {
            this.log.error("导出微件失败，微件名：" + folerName, var6);
         }

      });
   }

   @Override
   public List<AppWidget> parseWidgetsFromFile(MultipartFile file, boolean isUpdateSupport) throws IOException {
      if (file == null) {
         return null;
      } else {
         List<AppWidget> appWidgetList = new ArrayList<>();
         String filePath = FileUploadUtils.upload(this.fileProperties.getFullDataPath(), file);
         File zipFile = FileUploadUtils.getAbsoluteFile(this.fileProperties.getFullDataPath(), filePath);
         File unzip = ZipUtil.unzip(zipFile, CharsetKit.CHARSET_GBK);
         File[] widgetFolders = unzip.listFiles();
         if (widgetFolders != null) {

             for (File widgetFolder : widgetFolders) {
                 AppWidget appWidget = new AppWidget();
                 String widgetName = widgetFolder.getName();
                 if (widgetFolder.isDirectory()) {
                     File[] widgetFiles = widgetFolder.listFiles();
                     appWidget.setWidgetName(widgetName);
                     if (widgetFiles != null) {

                         for (File widgetFile : widgetFiles) {
                             String widgetFileName = widgetFile.getName();
                             if (widgetFile.isFile()) {
                                 if (Arrays.asList(WIDGET_FILE_RESOURCES).contains(widgetFileName)) {
                                     this.copyWidgetFileResource(widgetFile, widgetName, widgetFileName, isUpdateSupport);
                                 } else {
                                     String fileContent = FileUtil.readString(widgetFile, Charset.forName("UTF-8"));
                                     if ("manifest.json".equals(widgetFileName)) {
                                         appWidget.setManifest(fileContent);
                                     }

                                     if ("config.json".equals(widgetFileName)) {
                                         fileContent = fileContent.replace("\"/upload/", "\"/file/default/");
                                         appWidget.setConfig(fileContent);
                                     }
                                 }
                             } else if (widgetFile.isDirectory() && widgetFileName.equals("images")) {
                                 this.parseWidgetImageFiles(widgetName, widgetFile, isUpdateSupport);
                             }
                         }
                     }

                     if (StringUtils.isNotEmpty(appWidget.getManifest())) {
                         appWidgetList.add(appWidget);
                     }
                 }
             }
         }

         FileUtil.del(zipFile);
         FileUtil.del(unzip);
         return appWidgetList;
      }
   }

   @Override
   public String importWidgets(List<AppWidget> appWidgetList, boolean isUpdateSupport, String operName) {
      if (!StringUtils.isNull(appWidgetList) && !appWidgetList.isEmpty()) {
         int successNum = 0;
         int failureNum = 0;
         StringBuilder successMsg = new StringBuilder();
         StringBuilder failureMsg = new StringBuilder();

          for (AppWidget appWidget : appWidgetList) {
              try {
                  AppWidget w = this.appWidgetMapper.selectAppWidgetByWidgetName(appWidget.getWidgetName());
                  if (StringUtils.isNull(w)) {
                      BeanValidators.validateWithException(this.validator, appWidget, new Class[0]);
                      appWidget.setCreateBy(operName);
                      this.insertAppWidget(appWidget);
                      ++successNum;
                      successMsg.append("<br/>").append(successNum).append("、微件 ").append(appWidget.getWidgetName()).append(" 导入成功");
                  } else if (isUpdateSupport) {
                      BeanValidators.validateWithException(this.validator, appWidget, new Class[0]);
                      appWidget.setWidgetId(w.getWidgetId());
                      appWidget.setUpdateBy(operName);
                      this.updateAppWidget(appWidget);
                      ++successNum;
                      successMsg.append("<br/>").append(successNum).append("、微件 ").append(appWidget.getWidgetName()).append(" 更新成功");
                  } else {
                      ++failureNum;
                      failureMsg.append("<br/>").append(failureNum).append("、微件 ").append(appWidget.getWidgetName()).append(" 已存在");
                  }
              } catch (Exception var12) {
                  ++failureNum;
                  String msg = "<br/>" + failureNum + "、微件 " + appWidget.getWidgetName() + " 导入失败：";
                  failureMsg.append(msg).append(var12.getMessage());
                  this.log.error(msg, var12);
              }
          }

         if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
         } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
            return successMsg.toString();
         }
      } else {
         throw new ServiceException("导入微件数据不能为空！");
      }
   }

   private void parseWidgetImageFiles(String widgetName, File widgetFile, boolean isUpdateSupport) {
      File[] imageFiles = widgetFile.listFiles();
      if (imageFiles != null) {

          for (File imageFile : imageFiles) {
              String imageFileName = imageFile.getName();
              if (imageFile.isFile() && !imageFileName.equals(".gitkeep")) {
                  this.copyWidgetFileResource(imageFile, widgetName, "images" + File.separator + imageFileName, isUpdateSupport);
              }
          }
      }

   }

   private void copyWidgetFileResource(File widgetFile, String widgetName, String resourceName, boolean isUpdateSupport) {
      String fileFullPath = this.fileProperties.getFullDataPath() + File.separator + "widgets" + File.separator + widgetName + File.separator + resourceName;
      File copyFile = new File(fileFullPath);
      if (!copyFile.exists()) {
         FileUtil.copyContent(widgetFile, copyFile, true);
      } else if (isUpdateSupport) {
         FileUtil.copyContent(widgetFile, copyFile, true);
      }

   }

   @Override
   public Map<String, File> listFileResources(String resourceType, String resourcePath) {
      String defaultFileResourceFullPath = Paths.get(this.fileProperties.getFullPath(), "default", resourceType, resourcePath).normalize().toString();
      String fileResourceFullPath = Paths.get(this.fileProperties.getFullDataPath(), resourceType, resourcePath).normalize().toString();
      List<File> defaultFileResources = FileUtil.loopFiles(defaultFileResourceFullPath);
      List<File> fileResources = FileUtil.loopFiles(fileResourceFullPath);
      Map<String, File> defaultFileResourceMap = defaultFileResources.stream()
              .collect(Collectors.toMap((fileResource) -> fileResource.getPath().replace(defaultFileResourceFullPath, ""), (fileResource) -> fileResource));
      Map<String, File> fileResourceMap = fileResources.stream()
              .collect(Collectors.toMap((fileResource) -> fileResource.getPath().replace(fileResourceFullPath, ""), (fileResource) -> fileResource));
      defaultFileResourceMap.putAll(fileResourceMap);
      return defaultFileResourceMap;
   }
}
